import 'dart:async';
import 'dart:io';
import 'dart:math';

import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'package:in_app_purchase_storekit/in_app_purchase_storekit.dart';
import 'package:in_app_purchase_storekit/store_kit_wrappers.dart';
import 'package:jet_rails/jr_app/jr_models/jr_good_model.dart';
import 'package:jet_rails/jr_app/jr_models/jr_transaction_model.dart';
import 'package:jet_rails/jr_app/jr_views/jr_user/jr_user_controller.dart';
import 'package:jet_rails/jr_services/jr_apis.dart';
import 'package:jet_rails/jr_services/jr_http.dart';
import 'package:jet_rails/jr_services/jr_services.dart';
import 'package:jet_rails/jr_utils/jr_logger.dart';
import 'package:jet_rails/jr_utils/jr_toast.dart';

class JrPayment {
  static JrPayment? _instance;

  static JrPayment getInstance() {
    _instance ??= JrPayment._internal();
    return _instance!;
  }

  JrPayment._internal();

  factory JrPayment() => getInstance();

  Future<void> buyTheGood(JrGoodsModel goodItem) async {
    try {
      EasyLoading.show(dismissOnTap: false);
      await Future.delayed(Duration(milliseconds: 500 + Random().nextInt(500)));

      Map map = await JrApis.createTransaction(goodItem.code!);
      JrTransactionModel transactionModel = JrTransactionModel.fromJson(Map<String, dynamic>.from(map));
      await _applePay(transactionModel);
    } catch (e, s) {
      JrLogger.e('buyTheGood error $e, $s');
    } finally {
      EasyLoading.dismiss();
    }
  }

  static StreamSubscription<List<PurchaseDetails>>? _subscription;

  static Future<void> _applePay(JrTransactionModel transactionModel) async {
    _subscription?.cancel();
    _subscription = InAppPurchase.instance.purchaseStream.listen(
      (List<PurchaseDetails> purchaseDetailsList) {
        _listenToPurchaseOnData(purchaseDetailsList, transactionModel);
      },
      onDone: () {
        _subscription?.cancel();
      },
      onError: (Object error) {
        _subscription?.cancel();
        JrToast.show(error.toString());
      },
    );

    if (await InAppPurchase.instance.isAvailable() == false) {
      JrToast.show('App store is unavailable, please check your network.');
      EasyLoading.dismiss();
      return;
    }

    if (Platform.isIOS) {
      InAppPurchaseStoreKitPlatformAddition iosAdditional =
          InAppPurchase.instance.getPlatformAddition<InAppPurchaseStoreKitPlatformAddition>();
      await iosAdditional.setDelegate(IOSPaymentQueueDelegate());
    }

    ProductDetailsResponse products = await InAppPurchase.instance.queryProductDetails({transactionModel.goodsCode});
    if (products.error != null) {
      JrToast.show('Failed to get product information from Apple.');
      EasyLoading.dismiss();
      return;
    }
    if (products.productDetails.isEmpty) {
      JrToast.show('No products');
      EasyLoading.dismiss();
      return;
    }

    // Query success from apple
    ProductDetails detail = products.productDetails.first;
    PurchaseParam param = PurchaseParam(productDetails: detail, applicationUserName: transactionModel.orderNo);
    // Request to Apple INC
    bool isSuccess = await InAppPurchase.instance.buyConsumable(purchaseParam: param);
  }

  static Future<void> _listenToPurchaseOnData(
      List<PurchaseDetails> purchaseDetailsList, JrTransactionModel transaction) async {
    for (final PurchaseDetails purchaseDetails in purchaseDetailsList) {
      JrLogger.d(
          '===>>>_listenToPurchaseOnData  status=${purchaseDetails.status}  productID=${purchaseDetails.productID}  '
          'transactionDate=${purchaseDetails.transactionDate} '
          'purchaseID=${purchaseDetails.purchaseID}  transactionDate=${purchaseDetails.transactionDate}'
          'verificationData.localVerificationData=${purchaseDetails.verificationData.localVerificationData}'
          'verificationData.serverVerificationData=${purchaseDetails.verificationData.serverVerificationData}'
          'verificationData.source=${purchaseDetails.verificationData.source}');
      if (purchaseDetails.status == PurchaseStatus.pending) {
        // do nothing
      } else if (purchaseDetails.status == PurchaseStatus.canceled) {
        InAppPurchase.instance.completePurchase(purchaseDetails);
      } else {
        if (purchaseDetails.status == PurchaseStatus.error) {
          JrToast.show('Purchase error');
          InAppPurchase.instance.completePurchase(purchaseDetails);
        } else if (purchaseDetails.status == PurchaseStatus.purchased ||
            purchaseDetails.status == PurchaseStatus.restored) {
          if (purchaseDetails.pendingCompletePurchase) {
            await InAppPurchase.instance.completePurchase(purchaseDetails);
          }
          if (purchaseDetails.productID == transaction.goodsCode) {
            await rechargeCoins(purchaseDetails, transaction);
          }
        }
      }
    }
  }

  static Future<void> rechargeCoins(PurchaseDetails purchaseDetails, JrTransactionModel orderModel) async {
    try {
      EasyLoading.show(dismissOnTap: false);
      String code = orderModel.goodsCode;
      String orderNo = orderModel.orderNo;
      String transactionId = purchaseDetails.purchaseID!;
      String payload = purchaseDetails.verificationData.serverVerificationData;
      try {
        await JrApis.verifyTransaction(orderNo, payload, transactionId);
      } catch (e, s) {
        JrLogger.e('rechargeCoins error: $e\n$s');
        String msg = (e is JrHttpException ? e.bodyMsg : null) ?? '';
        JrToast.show('Verify Payment Failed. $msg');
        rethrow;
      }

      for (JrGoodsModel good in Get.find<JrUserController>().goodsList.value) {
        if (good.code == code) {
          JrServices.instance.loginUser.value.availableCoins += good.exchangeCoin ?? 0;
          JrServices.instance.refreshLoginUser();

          JrToast.show('Purchase success');
          return;
        }
      }
    } catch (e, s) {
      JrLogger.e('Recharge coins error: $e, $s');
    } finally {
      EasyLoading.dismiss();
    }
  }
}

class IOSPaymentQueueDelegate implements SKPaymentQueueDelegateWrapper {
  @override
  bool shouldShowPriceConsent() => false;

  @override
  bool shouldContinueTransaction(SKPaymentTransactionWrapper transaction, SKStorefrontWrapper storefront) => true;
}
